module bmp_gen_lib;

/* rgb_565 to rgb_888 */
function [23:0] rgb_888 (
    input [15:0] rgb_565
);
begin
    rgb_888[23:16] = rgb_565[15:11] / 31.0 * 255.0; //r
    rgb_888[15:8]  = rgb_565[10:5]  / 63.0 * 255.0; //g
    rgb_888[7:0]   = rgb_565[4:0]   / 31.0 * 255.0; //b
end
endfunction

/* big to small endian */
function [31:0] to_small_endian(
    input [31:0] big_endian
);
begin
    to_small_endian[31:24] = big_endian[7:0];
    to_small_endian[23:16] = big_endian[15:8];
    to_small_endian[15:8]  = big_endian[23:16];
    to_small_endian[7:0]   = big_endian[31:24];
end
endfunction

/* num to char: 5->"5" */
function [7:0] num_to_ascii(
    input [3:0] num
);
    num_to_ascii[7:0] = num + "0";
endfunction

/* num to filename string: 7890->"7890.bmp" */
function [8*8-1:0] num_to_filename(
    input [13:0] num
);
begin
    //not use system function
    num_to_filename[8*8-1 : 8*8-8] = num_to_ascii(num / 1000);   //1
    num_to_filename[7*8-1 : 7*8-8] = num_to_ascii(num / 100 % 10); //2
    num_to_filename[6*8-1 : 6*8-8] = num_to_ascii(num / 10 % 10);  //3
    num_to_filename[5*8-1 : 5*8-8] = num_to_ascii(num % 10);     //4
    num_to_filename[4*8-1 : 0    ] = ".bmp";
    //don't use system function
    // $sformat(num_to_filename, "%4d.bmp", num);  //不会自动补0，使用空格代替
    // num_to_filename = $sformatf("%04d.bmp", num);   //system verilog 
end
endfunction

function [31:0] width_4byte_align(
    input [31:0] width
);
    width_4byte_align = ((width * 24) / 8 + 3) / 4 * 4;
endfunction

//close bmp file
task bmp_close(
    input integer fd,
    input integer num
);
begin
    $fclose(fd);
    $display("close: %4d.bmp", num);
end
endtask

//bmp write rgb data 565 format
task bmp_write(
    input integer fd,
    input [15:0] rgb_data_565
);
    reg [23:0] rgb_data_888;
    begin
        rgb_data_888 = rgb_888(rgb_data_565);
        // B G R
        $fwrite(fd, "%c%c%c", rgb_data_888[7:0], rgb_data_888[15:8], rgb_data_888[23:16]);
    end
endtask

//write num zero
task bmp_write_zero(
    input integer fd,
    input integer zero_num
);
integer idx;
begin
    if(zero_num > 0)
    begin
        for(idx = 0; idx < zero_num; idx = idx + 1)
            // $fwrite(fd, "%c", 8'h12);
            $fwrite(fd, "%c", 8'h00);
    end
end
endtask

task bmp_new(
     input [13:0] filenum
    ,input [31:0] width
    ,input [31:0] height
    
    ,output reg [31:0] width_real
    ,output integer fd
);
    /* file header(14 Byte) and map info(40 Byte) */
    reg [7:0] bmp_head_map [53:0]; 
    reg [31:0] bfSize;  /* = w*h*3*/
    reg [31:0] width_small;
    reg [31:0] height_small;
    reg [8*8-1:0] filename;
    reg [23:0] rgb_data_888;
    integer idx;
    
    begin
        //width 4 byte align
        width_real = width_4byte_align(width);
        // $display("width: %4d, width_real: %4d", width, width_real);
        //width and height to small endian
        width_small  = to_small_endian(width);
        height_small = to_small_endian(height);
        
        bfSize       = to_small_endian(width_real * height + 54 + 2);
        filename     = num_to_filename(filenum);
        
        bmp_head_map[0 ] = 'h42;
        bmp_head_map[1 ] = 'h4d;
        //bfSize
        bmp_head_map[2 ] = bfSize[31:24];
        bmp_head_map[3 ] = bfSize[23:16];
        bmp_head_map[4 ] = bfSize[15:8] ;
        bmp_head_map[5 ] = bfSize[7:0]  ;
        
        bmp_head_map[6 ] = 'h0;
        bmp_head_map[7 ] = 'h0;
        bmp_head_map[8 ] = 'h0;
        bmp_head_map[9 ] = 'h0;
        
        bmp_head_map[10] = 'h36;
        bmp_head_map[11] = 'h0;
        bmp_head_map[12] = 'h0;
        bmp_head_map[13] = 'h0;
        
        bmp_head_map[14] = 'h28;
        bmp_head_map[15] = 'h0;
        bmp_head_map[16] = 'h0;
        bmp_head_map[17] = 'h0;
        //width
        bmp_head_map[18] = width_small[31:24];
        bmp_head_map[19] = width_small[23:16];
        bmp_head_map[20] = width_small[15:8] ;
        bmp_head_map[21] = width_small[7:0]  ;
        //height
        bmp_head_map[22] = height_small[31:24];
        bmp_head_map[23] = height_small[23:16];
        bmp_head_map[24] = height_small[15:8] ;
        bmp_head_map[25] = height_small[7:0]  ;
        
        bmp_head_map[26] = 'h01;
        bmp_head_map[27] = 'h0;
        //24Bit RGB_888
        bmp_head_map[28] = 'h18;
        bmp_head_map[29] = 'h0;
        
        bmp_head_map[30] = 'h0;
        bmp_head_map[31] = 'h0;
        bmp_head_map[32] = 'h0;
        bmp_head_map[33] = 'h0;
        bmp_head_map[34] = 'h0;
        bmp_head_map[35] = 'h0;
        bmp_head_map[36] = 'h0;
        bmp_head_map[37] = 'h0;
        bmp_head_map[38] = 'h0;
        bmp_head_map[39] = 'h0;
        bmp_head_map[40] = 'h0;
        bmp_head_map[41] = 'h0;
        bmp_head_map[42] = 'h0;
        bmp_head_map[43] = 'h0;
        bmp_head_map[44] = 'h0;
        bmp_head_map[45] = 'h0;
        bmp_head_map[46] = 'h0;
        bmp_head_map[47] = 'h0;
        bmp_head_map[48] = 'h0;
        bmp_head_map[49] = 'h0;
        bmp_head_map[50] = 'h0;
        bmp_head_map[51] = 'h0;
        bmp_head_map[52] = 'h0;
        bmp_head_map[53] = 'h0;
        
        fd = $fopen(filename,"wb+"); //write binary
        
        if(fd != 0) begin
            $display("new: %8s, width: %4d, height: %4d", filename, width, height);
            for(idx = 0; idx <= 53; idx = idx + 1)
                $fwrite(fd, "%c", bmp_head_map[idx]);
        end
        else 
            $display("file create failed: %d", fd);
    end
endtask

`ifdef TEST_BMP_BEN_10X5

    integer fd = 0, i = 0, j = 0;
    reg [31:0] width_real = 0;
    reg [31:0] w = 0, h = 0;

    initial 
    begin
        w = 10;
        h = 5;

        bmp_new(1234, w, h, width_real, fd);

        // $fseek(fd, 54, 0);  //from 55 start, 绝对位置
        // $fseek(fd, 54, 1);  //fd+54 start, 相对位置
        
        //倒序写入, 先写第1行数据
        for(i = 5; i > 0; i = i - 1) begin
            $fseek(fd, 54 + width_real*(i-1), 0); 
            
            for(j = 1; j <= w; j = j + 1)
                bmp_write(fd, {5'h1f, h-i,5'h1f});
            bmp_write_zero(fd, width_real-w*3);
        end
        
        /*
            10x5 forexmple:
            header: 0-53
            line 5: 54  -> 83    84  85
            line 4: 86  -> 115   116 117
            line 3: 118 -> 147  148 149     
            line 2: 150 -> 179  180 181
            line 1: 182 -> 211  212 123
                                214 215
        */

        bmp_write_zero(fd, 2);     //photoshop
        bmp_close(fd);
        $stop;
    end

`endif

endmodule